package com.huiyeji.dynamic.core.provider;

import com.huiyeji.dynamic.core.MethodBody;
import com.huiyeji.dynamic.core.MethodRequest;
import com.huiyeji.dynamic.core.MethodRequestMatchRule;
import com.huiyeji.dynamic.core.annotations.DynamicMethod;
import com.huiyeji.dynamic.core.annotations.Private;
import com.huiyeji.sort.MatchRule;
import com.huiyeji.sort.ObjectMatcher;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

public class DynamicAnnoMethodProvider implements DynamicMethodProvider{

    private static final MatchRule<MethodRequest,MethodRequest> RULE = new MethodRequestMatchRule();

    private final Map<String,ObjectMatcher<MethodRequest,MethodRequest, MethodBody>> matcherMap = new TreeMap<>();

    public DynamicAnnoMethodProvider(List<Method> methods){
        initDynamicMethods(methods);
    }

    @Override
    public Method get(MethodRequest request) {
        if (!matcherMap.containsKey(request.getName())){
            return null;
        }
        ObjectMatcher<MethodRequest, MethodRequest, MethodBody> matcher = matcherMap.get(request.getName());
        MethodBody methodBody = matcher.matchObject(request);
        if (methodBody == null){
            StringBuilder parameterNames = new StringBuilder();
            Class<?>[] parameterTypes = request.getParameterTypes();
            for (int i = 0; i < parameterTypes.length; i++) {
                if (parameterTypes[i] != null){
                    parameterNames.append(parameterTypes[i].getName());
                } else {
                    parameterNames.append("null");
                }

                if (i < parameterTypes.length - 1){
                    parameterNames.append(" , ");
                }
            }
            throw new NullPointerException("the dynamic method 【 "+request.getDynamicObjectName()+"."+request.getName()+" ( "+parameterNames+" ) 】 match error !");
        }
        return methodBody.getMethod();
    }

    private void initDynamicMethods(List<Method> methods) {
        for (Method method : methods){
            if (!method.isAnnotationPresent(Private.class)){
                String methodName = method.getName();
                if (method.isAnnotationPresent(DynamicMethod.class)){
                    String dynamicMethodName = method.getAnnotation(DynamicMethod.class).value();
                    methodName = "".equals(dynamicMethodName)?methodName:dynamicMethodName;
                }
                MethodBody methodBody = new MethodBody(methodName, method);
                if (matcherMap.containsKey(methodName)){
                    matcherMap.get(methodName).registerObject(methodBody);
                } else {
                    ObjectMatcher<MethodRequest,MethodRequest, MethodBody> matcher = new ObjectMatcher<>(RULE);
                    matcher.registerObject(methodBody);
                    matcherMap.put(methodName,matcher);
                }
            }
        }
    }
}
